// Copyright (C) 2010 and onwards Harik Shazeer Labs
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Authors: Noam Shazeer and Georges Harik

#include "jobqueue.h"

JobQueue * G_JQ;

void InitJobQueue() {
  if (!G_JQ) {
    G_JQ = new JobQueue();
    G_JQ->LaunchThreads(ConfigInt("num_worker_threads"));
  }
}

extern JobQueue * G_JQ;

void * JobQueueLaunchThread(void *q) {
  //cout << "Entered JobQueueLaunchThread" << endl;
  JobQueue * jq = (JobQueue *)q;
  jq->ThreadBody();
  return NULL;
}

struct ZeroTask : public JobQueue::Task {
  ZeroTask(void *location, uint64 size) {
    location_ = location; 
    size_ = size;
  }
  void Run() {
    bzero(location_, size_);
  }
  void *location_;
  uint64 size_;
};

void JobQueueZero(JobQueue * jq, void * location, uint64 bytes) {
  char * loc = (char *) location;
  if (bytes < 1000000) {
    bzero(loc, bytes);
    return;
  }
  uint64 t0 = TimeNS();
  uint64 num_shards = jq->NumThreads();
  JobQueue::Job j(jq);
  for (uint64 i=0; i<num_shards; i++) {
    uint64 begin = ShardBegin(bytes, num_shards, i);
    uint64 size = ShardEnd(bytes, num_shards, i) - begin;
    j.AddTask(new ZeroTask(loc + begin, size));
  }
  j.Wait();
  uint64 t1 = TimeNS();
  cout << "t: " << 1e-9 * (t1 - t0) << " "
       << "JobQueueZero:: " << Delimit(bytes) << "B ";
}

void JobQueueCopy
(JobQueue * jq, uint64 * destination, uint64 *source, uint64 words){
  if (words < 100000) {
    memcpy(destination, source, words * sizeof(uint64));
    return;
  }
  // cout << "Long copy words = " << words << endl;
  uint64 num_shards = jq->NumThreads();
  JobQueue::Job j(jq);
  for (uint64 i=0; i<num_shards; i++) {
    uint64 begin = ShardBegin(words, num_shards, i);
    uint64 size = ShardEnd(words, num_shards, i) - begin;
    j.AddTask(new CopyTask(destination + begin, source+begin, size));
  }
  j.Wait();
}

// Target may overlap the source (just like memmove)
void JobQueueMove
(JobQueue * jq, uint64 * destination, uint64 *source, uint64 words,
 uint64 *buffer, uint64 buffer_size) {
  if (destination == source) return;
  if (words < 100000) {
    memmove(destination, source, words * sizeof(uint64));
    return; }
  if (destination > source) {
    cout << "Can't handle forward move yet" << endl;
    CHECK(false); }
  if (destination + words <= source) {
    JobQueueCopy(jq, destination, source, words);
    return;
  }
  uint64 start = 0;
  while (start < words) {
    uint64 n = min(words - start, buffer_size);
    JobQueueCopy(jq, buffer, source + start, n);
    JobQueueCopy(jq, destination + start, buffer, n);
    start += n;
  }
}

#include "timer.h"

